﻿using HS.Log.Models;
using log4net.Core;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;

namespace HS.Log
{
    internal static class LogstashExtensions
    {
        public static StringBuilder WriteString(this StringBuilder sb, string name, object value)
        {
            return sb.Append($"\"{name}\":").WriteString(value.ToString());
        }
        private static StringBuilder WriteString(this StringBuilder sb, string value)
        {
            sb.Append('\"');

            int runIndex = -1;
            int l = value.Length;
            for (var index = 0; index < l; ++index)
            {
                var c = value[index];

                if (c != '\t' && c != '\n' && c != '\r' && c != '\"' && c != '\\')// && c != ':' && c!=',')
                {
                    if (runIndex == -1)
                        runIndex = index;

                    continue;
                }

                if (runIndex != -1)
                {
                    sb.Append(value, runIndex, index - runIndex);
                    runIndex = -1;
                }

                switch (c)
                {
                    case '\t': sb.Append("\\t"); break;
                    case '\r': sb.Append("\\r"); break;
                    case '\n': sb.Append("\\n"); break;
                    case '"':
                    case '\\': sb.Append('\\'); sb.Append(c); break;
                    default:
                        sb.Append(c);
                        break;
                }
            }

            if (runIndex != -1)
                sb.Append(value, runIndex, value.Length - runIndex);

            return sb.Append('\"');
        }
        public static StringBuilder WriteValueObject(this StringBuilder sb, string name, object value)
        {
            return sb.Append($"\"{name}\":{value}");
        }
        public static StringBuilder WriteMessage(this StringBuilder sb, LogstashEvent evt)
        {
            sb.WriteString(nameof(LogstashEvent.Message), evt.Message);

            if (evt.Exception != null)
            {
                sb.Append(",")
                  .Append("\"exception\":{")
                  .WriteString(nameof(LogstashException.Exception_Class), evt.Exception.Exception_Class).Append(",")
                  .WriteString(nameof(LogstashException.Exception_Message), evt.Exception.Exception_Message).Append(",")
                  .WriteString(nameof(LogstashException.Stacktrace), evt.Exception.Stacktrace)
                  .Append("}");
            }
            return sb;
        }

        public static LogstashEvent GetEvent(this LoggingEvent loggingEvent, string app = "")
        {
            var obj = new LogstashEvent
            {
                App = app,
                Version = 1,
                Timestamp = loggingEvent.TimeStamp.ToUniversalTime().ToLocalTime(),
                Source_Host = Environment.MachineName,
                Thread_Name = loggingEvent.ThreadName,
                Level = loggingEvent.Level.ToString(),
                Logger_Name = loggingEvent.LoggerName,
                Message = loggingEvent.RenderedMessage
            };
            // location information, if available
            if (loggingEvent.LocationInformation != null)
            {
                obj.@class = loggingEvent.LocationInformation.ClassName;
                obj.method = loggingEvent.LocationInformation.MethodName;
                obj.line_number = loggingEvent.LocationInformation.LineNumber;
            }
            if (loggingEvent.ExceptionObject != null)
            {
                obj.Exception = new LogstashException
                {
                    Exception_Class = loggingEvent.ExceptionObject.GetType().ToString(),
                    Exception_Message = loggingEvent.ExceptionObject.Message,
                    Stacktrace = loggingEvent.ExceptionObject.StackTrace
                };
            }
            return obj;
        }

        public static string GetEvent(this LoggingEvent loggingEvent, IList<LogstashFileld> list, string app)
        {
            if (list.Count <= 0)
            {
                return loggingEvent.GetEvent(app).ToJson();
            }
            IDictionary<string, object> doc = new Dictionary<string, object>();
            foreach (LogstashFileld field in list)
            {
                object value = field.Layout.Format(loggingEvent);
                doc.Add(field.Name, value);
            }
            return JsonConvert.SerializeObject(doc);
        }

        public static string ToJson(this LogstashEvent logstashEvent)
        {
            return JsonConvert.SerializeObject(logstashEvent);
        }
    }
}
